<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>[1.14]-统一的返回格式和结构：ret-data-msg | PhalApi(π框架) - PHP轻量级开源接口框架 - 助你创造价值！</title>
    <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
    <meta content="yes" name="apple-mobile-web-app-capable">
    <meta content="black" name="apple-mobile-web-app-status-bar-style">
    <meta content="telephone=no" name="format-detection">
    <meta content="email=no" name="format-detection">
    <meta name="Author" content="PhalApi,ecitlm,dogstar">
    <meta name="description" content="[1.14]-统一的返回格式和结构：ret-data-msg，PhalApi是一个PHP轻量级开源接口框架，致力于快速开发接口服务。支持HTTP/SOAP/RPC等协议，可用于搭建接口/微服务/RESTful接口/Web Services。我们不断更新，保持生气；为接口负责，为开源负责！并承诺永久免费！">
    <meta name="keywords" content="PhalApi,phalapi,phalapi接口开发,后台接口开发,后台接口开发框架,接口开发,接口框架,开源接口框架,PHP后台接口开发,PHP接口开发,PHP接口框架,PHP后台接口框架,phalapi官网,PHP接口框架,php接口开发框架,php接口开发,php web框架,π框架,π开发框架,π接口框架,π接口开发框架,api接口,php接口,,PHP接口框架,phalapi文档,phalapi wiki,PhalApi文档,phalapi在线文档,phalapi官方文档">

    <link rel="stylesheet" type="text/css" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" media="screen">
    <link rel="stylesheet" type="text/css" href="https://cdn.bootcss.com/font-awesome/4.7.0/css/font-awesome.css" media="screen">
    <link rel="stylesheet" href="../css/main.css" />
    <link rel="stylesheet" href="../css/loading_bar.css" />
    <link rel="icon" href="http://webtools.qiniudn.com/dog_catch.png" type="image/x-icon" />
    <script src="../js/pace.min.js"></script>
	
	<!-- 代码高亮 -->
	<link rel="stylesheet" href="../css/highlight.min.css">
	<script src="../js/highlight.min.js"></script>
	<script>hljs.initHighlightingOnLoad();</script>
    
</head>

<body>
    <!-- navbar start -->
    <div class="navbar navbar-default navbar-fixed-top" id="mainnav" role="navigation">
        <div class="container">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target=".navbar-collapse">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a class="navbar-brand website_name" href="/"><!-- <span>PhalApi</span> --><strong>PhalApi</strong></a>
            </div>
            <div class="navbar-collapse collapse" aria-expanded="false" style="height: 1px;">
                <ul class="nav navbar-nav navbar-right" id="nav_bar">
                    <li><a href="/"><span class="glyphicon glyphicon-home"></span></a></li>
                    <li><a href="http://qa.phalapi.net/">社区</a></li>
                    <li><a href="/download.html">下载</a></li>
                    <li class="active"><a href="/wikis/">文档</a></li>
                    <li><a href="http://www.ituring.com.cn/book/2405">书籍</a></li>
                    <li><a href="http://demo.phalapi.net/">体验</a></li>
                    <li><a href="/about.html">关于</a></li>
                    <li><a href="/donate.html">贡献</a></li>
                    <li><a href="/wikis/en/">English</a></li>
                </ul>
            </div>
            <!--/.nav-collapse -->
        </div>
    </div>
    <!-- navbar end -->


    <div id="content">
        <div class="container">
            <div class="row row-md-flex row-md-flex-wrap">
                <h4><a href="/wikis/%5B1.13%5D-%E7%BB%9F%E4%B8%80%E7%9A%84%E6%8E%A5%E5%8F%A3%E8%AF%B7%E6%B1%82%E6%96%B9%E5%BC%8F%EF%BC%9A_sevice=XXX.XXX.html">上一章</a>   <a href="/wikis/">文档首页</a>   <a href="/wikis/%5B1.32%5D-%E5%9C%A8%E7%BA%BF%E6%8E%A5%E5%8F%A3%E6%96%87%E6%A1%A3%EF%BC%9A%E6%B3%A8%E9%87%8A%E8%A7%84%E8%8C%83.html">下一章</a></h4>
<hr />
<p><em>表达，从简单开始。--《Robin Williams：写给大家看的设计书》</em>  </p>
<h2>1.14.1 统一返回的格式</h2>
<p>很明显地，默认情况下，我们选择了 <strong>JSON</strong> 作为统一的格式返回接口结果。这里简单说明一下选取JSON统一返回的原因：  </p>
<ul>
<li>JSON当前很流行，且普通接口都采用此格式返回</li>
<li>JSON在绝大部分开发语言中都支持，跨语言</li>
<li>
<p>JSON在浏览器浏览时，有可视化插件支持，如FF下：    </p>
<p><img src="http://webtools.qiniudn.com/20150411005257_74d77c625f39db43cab421787f9674f4" alt="apic" /></p>
</li>
</ul>
<h2>1.14.2 统一返回结构</h2>
<p>通常，我们正常情况下请求接口会返回类似：  </p>
<pre><code class="language-html">{
    "ret": 200,
    "data": {
        "title": "Default Api",
        "content": "PHPer您好，欢迎使用PhalApi！",
        "version": "1.1.0",
        "time": 1423142802
    },
    "msg": ""
}</code></pre>
<p>其中，ret表示为返回状态码，200表示成功；data为领域业务数据，由接口自定义；最后msg为错误的提示信息。下面分别解释之。</p>
<h3>(1)返回状态码 ret</h3>
<p>参照HTTP的状态码，特约定：  </p>
<ul>
<li>200：接口正常请求并返回</li>
<li>4XX：客户端非法请求</li>
<li>5XX：服务器运行错误</li>
</ul>
<h4>200 正常返回</h4>
<p>当返回200时，需要同时返回data部分数据，以便客户端实现所需要的业务功能。  </p>
<h4>4XX 客户端非法请求</h4>
<p>此类请求是由客户端不正确调用引起的，如请求的接口服务不存在，或者接口参数不对，验证失败等等。当这种情况发生时，客户端同学只需要调整修正调用即可。  </p>
<p>对于此系统的状态码，在进行接口开发时，可由项目自已定义约定。  通常地，我们需要告知客户端签名失败时，可以这样：  </p>
<pre><code class="language-php">throw new PhalApi_Exception_BadRequest('wrong sign', 1);</code></pre>
<p>即抛出PhalApi_Exception_BadRequest异常即可，错误信息会返回客户端，对应msg字段；状态为1，系统对此类的异常会在400基础上相加的，即： <strong>401 = 400 + 1</strong> 。  </p>
<h4>5XX 服务器运行错误</h4>
<p>此类错误是应该避免的，但当客户端发现有这种情况时，应该知会后台接口开发人员进行修正。<br />
如当配置的参数规则不符合要求时，或者获取了不存在的参数等即会触发此类异常错误，通常由框架抛出。  </p>
<h3>(2)业务数据 data</h3>
<p>data为接口和客户端主要沟通对接的数据部分，可以为任何类型，由接口自定义。但为了更好地扩展、向后兼容，建议都使用array。  </p>
<h4>返回格式的定义与在线查看</h4>
<p>当我们在开发接口时，可以通过为接口添加注释的方式来定义接口的返回格式，然后就可以为外部提供在线文档的实时查看了。  </p>
<p>如：</p>
<pre><code class="language-php">&lt;?php

class Api_User extends PhalApi_Api {

    /**
     * 获取用户基本信息
     * @desc 用于获取单个用户基本信息
     * @return int code 操作码，0表示成功，1表示用户不存在
     * @return object info 用户信息对象
     * @return int info.id 用户ID
     * @return string info.name 用户名字
     * @return string info.note 用户来源
     * @return string msg 提示信息
     */
    public function getBaseInfo() {
        // ... ...
    }
</code></pre>
<p>然后在浏览器访问：<br />
<a href="http://demo.phalapi.net/checkApiParams.php?service=User.getBaseInfo">http://demo.phalapi.net/checkApiParams.php?service=User.getBaseInfo</a>  </p>
<p>可以看到：<br />
<img src="http://7xiz2f.com1.z0.glb.clouddn.com/20170205152848.jpg" alt="apic" />  </p>
<h4>注释格式</h4>
<p>格式是以docs的 return 注释来标明的，其格式为：</p>
<pre><code>@return  返回的类型  字段名字路径(以点号连接)  字段名字及解析</code></pre>
<p>其中，返回的类型可以为：  </p>
<table class="table table-bordered">
<thead>
<tr>
<th>关键字</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>string</td>
<td>字符串</td>
</tr>
<tr>
<td>int</td>
<td>整型</td>
</tr>
<tr>
<td>float</td>
<td>浮点型</td>
</tr>
<tr>
<td>boolean</td>
<td>布尔型</td>
</tr>
<tr>
<td>date</td>
<td>日期</td>
</tr>
<tr>
<td>array</td>
<td>数组</td>
</tr>
<tr>
<td>fixed</td>
<td>固定值</td>
</tr>
<tr>
<td>enum</td>
<td>枚举类型</td>
</tr>
<tr>
<td>object</td>
<td>对象</td>
</tr>
</tbody>
</table>
<blockquote>
<p>温馨提示：array与object的区别<br />
array是指没有下标的一个数组集合，或者有下标但下标是连续的自然数，且各元素的结构相同；object则是指一个结构体，类似字典。  </p>
</blockquote>
<p>此外，为了明确数组与对象间的返回格式，我们也推荐如果是元素来自数组，则在返回字段的后面添加方括号来表明，以提醒客户端在接收到此类返回时需要循环处理。如：</p>
<pre><code class="language-php">     * @return array list 用户列表
     * @return int list[].id 用户ID
     * @return string list[].name 用户名字
     * @return string list[].note 用户来源
</code></pre>
<p>当需要对接口进行更多说明时，可使用@desc注释，即：</p>
<pre><code>     * @desc 用于获取单个用户基本信息</code></pre>
<h3>(3)错误信息 msg</h3>
<p>当返回状态码不为200时，此字段不为空。即当有异常（如上面所说的客户端非法请求和服务端运行错误两大类）触发时，会自动将异常的错误信息作为错误信息msg返回。  </p>
<p>但对于服务端的异常，出于对接口隐私的保护，框架在错误信息时没有过于具体地描述；相反，对于客户端的异常，由会进行必要的说明，以提醒客户端该如何进行调用调整。  </p>
<p>此外，我们根据需要可以考虑是否需要进行国际化的翻译。如果项目在可预见的范围内需要部署到国外时，提前做好翻译的准备是很有帮助的。如下，开发时可以这样返回异常错误信息：</p>
<pre><code class="language-php">throw new PhalApi_Exception_BadRequest(T('wrong sign'), 1);</code></pre>
<h3>(4)头部信息 header</h3>
<p>当使用的是HTTP/HTTPS协议时，并且需要设置头部header时，可以使用<code>PhalApi_Response::addHeaders()</code>进行设置。对于JSON格式的返回默认设置的头部有：  </p>
<pre><code>Content-Type:"application/json;charset=utf-8"</code></pre>
<p>若需要其他的头部设置，可类似这样，在<code>init.php</code>初始化文件进行设置跨域访问（但通常不建议允许全部跨域，这里仅作演示）：  </p>
<pre><code class="language-php">DI()-&gt;response-&gt;addHeaders('Access-Control-Allow-Origin', '*');</code></pre>
<p>后面相同的头部信息会覆盖前面的。  </p>
<h3>(5)将ret状态码转换成相应的Http状态码输出</h3>
<p>默认情况下，PhalApi返回的Http状态码都为200，不管ret值为多少。当需要将Http状态码与ret结果状态码时对应时，可以这样使用：  </p>
<pre><code class="language-php">$api = new PhalApi();
$rs = $api-&gt;response();

// 在输出前，调整Http状态码
$rs-&gt;adjustHttpStatus()-&gt;output();</code></pre>
<p>然后，PhalApi将会根据ret值自动转换。  </p>
<p>当抛出4xx系列异常时，如：  </p>
<pre><code class="language-php">throw new PhalApi_Exception_BadRequest('无访问权限', 3); // 403 = 400 + 3</code></pre>
<p>会得到以下输出： </p>
<pre><code>HTTP/1.1 403 Forbidden

{
    "ret": 403,
    "data": [],
    "msg": "非法请求：无访问权限"
}</code></pre>
<p>若希望在状态码不为200情况下也返回业务数据data，或者不想使用抛出异常的方式但同样也能返回业务数据data，这时，可以手动设置ret状态码，例如：  </p>
<pre><code class="language-php">class Api_Default extends PhalApi_Api {
    public function index() {
        DI()-&gt;response-&gt;setRet(403);
        return array('name' =&gt; 'PhalApi');
    }
}</code></pre>
<p>会得到以下输出： </p>
<pre><code>HTTP/1.1 403 Forbidden

{
    "ret": 403,
    "data": {
        "name": "PhalApi"
    },
    "msg": ""
}</code></pre>
<p>若还需要追加msg错误提示信息，可以使用<code>setMsg()</code>方法，如这样：  </p>
<pre><code class="language-php">DI()-&gt;response-&gt;setRet(403)-&gt;setMsg('缺少访问权限');</code></pre>
<p>请注意，手动设置的状态码和错误信息，当抛出异常时，PhalApi会以所抛出异常的错误码和错误信息为准。  </p>
<blockquote>
<p>温馨提示：根据ret调整Http状态，需要PhalApi 1.4.1 及以上版本支持。</p>
</blockquote>
<h2>1.14.3 关于Exception类异常没捕捉的原因</h2>
<p>我们没有对Exception类的异常进行捕捉，封装返回非200的形式，是因为我们出于以下的考虑：</p>
<ul>
<li>一来为了方便开发过程中快速发现及定位具体出错的位置；</li>
<li>二来为了便于线上环境中nginx服务器对错误的捕捉和纪录；</li>
</ul>
<h2>1.14.4 JsonP格式和其他的返回</h2>
<p>在部分H5页面异步请求的情况下，客户端需要我们返回JSONP格式的结果，则可以这样在入口文件重新注册response：</p>
<pre><code class="language-php">if (!empty($_GET['callback'])) {
    DI()-&gt;response = new PhalApi_Response_JsonP($_GET['callback']);
}</code></pre>
<p>但是在测试环境中，我们是不希望有内容输出的，所以我们可以测试时这样注册response：</p>
<pre><code class="language-php">DI()-&gt;response = 'PhalApi_Response_Explorer';</code></pre>
<h2>1.14.5 扩展你的返回格式</h2>
<p>当你的项目需要返回其他格式时，如返回XML，则可以先这样实现你的格式类：</p>
<pre><code class="language-php">class MyResponse_XML extends PhalApi_Response {

    protected function formatResult($result) {
        //TODO：把数组$result格式化成XML ...
    }
}</code></pre>
<p>随后，也是简单重新注册一下即可：</p>
<pre><code class="language-php">DI()-&gt;response = new MyResponse_XML();</code></pre>
<h2>1.14.6 各状态码产生的时机</h2>
<p><img src="http://webtools.qiniudn.com/20150413210910.jpg" alt="各状态码产生的时机" />  </p>
<h2>1.14.7 更好地建议</h2>
<p>很多时候，很多业务场景，客户端在完成一个接口请求并获取到所需要的数据后，需要进行不同的处理的。 </p>
<ul>
<li>就登录来说，当登录失败时，可能需要知道：</li>
<li>是否用户名不存在？</li>
<li>是否密码错误？</li>
<li>是否已被系统屏蔽？</li>
<li>是否密码错误次数超过了最大的重试次数？</li>
<li>...</li>
</ul>
<p>显然，这里也有一个返回状态码，更准备来说，是业务操作状态码。并且，此类的状态依接口不同而不同，很难做到统一。  </p>
<p>SO？  </p>
<p>我们建议的是，项目接口在业务数据data里面统一再定义一个状态码，通常为code字段，完整路径即： <strong>data.code</strong> ，同时为0时表示操作成功，非0时为不同的失败场景。如上面的登录：</p>
<ul>
<li>code = 0 登录成功</li>
<li>code = 1 用户名不存在</li>
<li>code = 2 密码错误</li>
<li>code = 3 系统已屏蔽此账号</li>
<li>code = 4 密码错误次数超过了最大的重试次数</li>
<li>...</li>
</ul>
<p>最后，客户端在获取到接口返回的数据后，先统一判断ret是否正常请求并正常返回，即ret = 200；若是，则再各自判断操作状态码code是否为0，如果不为0，则提示相应的文案并进行相应的引导，如果为0，则走正常流程！</p>
<h2>1.14.8 领域特定设计与Fiat标准</h2>
<p>在《RESTful Web APIs》一书中提及到，标准可以划归到4个分类，分别是：fiat标准、个人标准、公司标准以及开放标准。  </p>
<p>显然，我们这里推荐的是 <strong>JSON + ret-data-msg</strong> 返回格式既不是个人标准，也不是公司标准（就笔者观察的范筹而言，未发现某个公司定义了此格式）。而且，也不属于开放标准，因为也还没达到此程度。更多的，它是fiat标准。<br />
我们很容易发现，身边的应用、系统以及周围项目都在使用诸如此类的返回结构格式，如一些AJAX的接口。  </p>
<p>当然，我们可希望可以消除语义上的鸿沟，以便在后台接口开发上有一个很好地共识。  </p>
<p>同时，<strong>JSON + ret-data-msg</strong> 返回格式也是一种领域特定的格式，它更多是为app多端获取业务数据而制作的规范。虽然它不是很完美，不具备自描述消息，也没有资源链接的能力，但我们认为它是一种恰到好处的格式。<br />
在基于JSON通用格式的基础上，加以 <strong>ret-data-msg</strong> 的约束，它很好地具备了统一性，可能门槛低，容易理解。</p>
<hr />
<h4><a href="/wikis/%5B1.13%5D-%E7%BB%9F%E4%B8%80%E7%9A%84%E6%8E%A5%E5%8F%A3%E8%AF%B7%E6%B1%82%E6%96%B9%E5%BC%8F%EF%BC%9A_sevice=XXX.XXX.html">上一章</a>   <a href="/wikis/">文档首页</a>   <a href="/wikis/%5B1.32%5D-%E5%9C%A8%E7%BA%BF%E6%8E%A5%E5%8F%A3%E6%96%87%E6%A1%A3%EF%BC%9A%E6%B3%A8%E9%87%8A%E8%A7%84%E8%8C%83.html">下一章</a></h4><div style="float: left">
<h4>
<a href="http://qa.phalapi.net/">还有疑问？欢迎到社区提问！</a>
</h4>
</div>
            </div>
        </div>
    </div>

 <!-- 广告位 -->
<div class="grid-wrapper desktop-only">
	<p align="center">
		<a href="http://7xiz2f.com1.z0.glb.clouddn.com/%E6%88%91%E7%9A%84%E5%90%8D%E5%AD%97%E5%8F%AB%EF%BC%9A%CF%80%E6%A1%86%E6%9E%B6%20-%20PhalApi%202016%E5%B9%B4%E5%BA%A6%E5%BC%80%E6%BA%90%E6%80%BB%E7%BB%93%20-%20%E5%AE%98%E6%96%B9%E5%87%BA%E5%93%81.pdf" target="blank"><img width="950" height="100" src="http://7xiz2f.com1.z0.glb.clouddn.com/ad_20170104.png"></a>
		<a href="http://www.itran.cc/" target="blank"><img width="950" height="100" src="http://7xslqv.com1.z0.glb.clouddn.com/images/9/69/b88bc92455dc1239f9a5bf8d72929.png"></a>
		<a href="http://web-tools.phalapi.net/" target="blank"><img width="950" height="100" src="http://7xslqv.com1.z0.glb.clouddn.com/images/7/d0/88f523566c482296aecc43d185ca2.png"></a>
	</p>
</div>

        <!-- footer -->
        <div class="footer">
            <div class="link container ">
                <div class=" col-xs-7 col-sm-7 col-md-7 col-lg-7 ">
                    <h3>PhalApi (π框架)</h3>
                    <p>一个轻量级PHP开源接口框架，专注于接口服务开发，支持HTTP/SOAP/RPC协议，拥有自动生成的在线文档、多种开发语言的客户端SDK包以及可重用的扩展类库，可用于快速搭建微服务、RESTful接口或Web
                        Services。</p>
                    <!-- <p>:</p> -->
                    <span>友情链接:</span>
                    <a href="https://www.phalapi.net/" target="_blank"> PhalApi </a>
                    <a href="http://www.oschina.net/" target="_blank"> 开源中国 </a>
                    <a href="https://www.itran.cc/" target="_blank">艾翻译</a>
                    <a href="http://www.phalconphp.com/en/" target="_blank">Phalcon</a>
                    <a href="https://phpunit.de/manual/3.7/zh_cn/automating-tests.html" target="_blank">PHPUnit</a>
                    <a href="http://www.thoughtworks.com/cn/" target="_blank">ThoughtWorks</a>
                    <a href="mailto:chanzonghuang@gmail.com">友链交换</a>

                </div>
                <div class=" col-xs-5 col-sm-5 col-md-5 col-lg-5 contact_us ">
                    <h3>联系我们</h3>
                    <a href="https://github.com/phalapi/phalapi"><img src="../images/github.png" alt="github" /></a>
                    <a href="http://weibo.com/p/100808d236e99beb645bfb56ed1c37dde9b8bd?k=phalapi%E5%BC%80%E6%BA%90%E6%A1%86%E6%9E%B6&from=501&_from_=huati_topic"><img src="../images/weibo.png"  alt="weibo" /></a>
                    <a href="http://7xslqv.com1.z0.glb.clouddn.com/images/1/b0/894fde762850cb11250253f7d5ede.png"><img src="../images/zfb.png"  alt="zfb" /></a>
                    <a href="https://jq.qq.com/?_wv=1027&k=4A6reum"><img src="../images/qq.png"  alt="qq" /></a>
					<a href="http://git.oschina.net/dogstar/PhalApi"><img src="../images/git-oschina.png"  alt="git-oschina" /></a>
                </div>
                <div class="cls"></div>
                <div class="text-center copy_right"> ©2015-2017 PhalApi All Rights Reserved. <a href="http://www.miitbeian.gov.cn" target="_blank">粤ICP备15028808号</a>

				<script type="text/javascript">var cnzz_protocol = (("https:" == document.location.protocol) ? " https://" : " http://");document.write(unescape("%3Cspan id='cnzz_stat_icon_1255326144'%3E%3C/span%3E%3Cscript src='" + cnzz_protocol + "s4.cnzz.com/z_stat.php%3Fid%3D1255326144%26show%3Dpic' type='text/javascript'%3E%3C/script%3E"));</script>
                </div>
            </div>
        </div>
    </div>


    <a href="https://github.com/phalapi"><img id="gitHub_fllow" style="position: fixed; top: 0; right: 0; border: 0;z-index:9999" src="https://camo.githubusercontent.com/652c5b9acfaddf3a9c326fa6bde407b87f7be0f4/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f6f72616e67655f6666373630302e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_orange_ff7600.png"></a>


    <script src="../js/jquery-1.11.1.min.js"></script>
    <script src="../js/bootstrap.min.js"></script>
</body>

</html>